Repository navigation
Quadratic paths made linear; Headers.get, regex crashes and five wrong results fixed - #96
Merged
Merged
Conversation
…g results fixed
Speed. A string's length is a strlen call and an array's shift moves every
element left, and both sat inside loops:
- toLowerCase/toUpperCase, JSON.stringify of a string, charAt and the other
string helpers ask the length once (200k characters: 898 ms to under 1 ms).
- Array join builds its parts and allocates once (20k items: 2671 ms to 2 ms).
- Array sort is a stable merge sort of positions applied by exchanges, where it
was a selection sort (default sort of 8k ints: 6080 ms to 15 ms).
- flatMap, toSpliced and Array<T>.push move elements out with a reverse and
pops (flatMap of 80k: 2394 ms to under 1 ms).
- MessageQueue.take pops from an outbox filled from the inbox when it runs
empty (80k items: 2375 ms to 6 ms).
- Array<T>.splice removes by two reversals, and unshift and splice insert
eight or more items by rotation; fewer keep the native move.
Null and crashes.
- Headers: the private methods come after the public ones. lib.d.ts declares
only the public ones and a program finds a method by its place, so every call
ran a method two places off and get returned garbage. get now returns
string | null.
- RegExp.split is declared in lib.d.ts, the same kind of mismatch.
- replaceAll with an invalid pattern throws a SyntaxError where it read a null.
- exec and test with lastIndex past the end fail the match and reset it to 0
where they read past the string.
- regexp_exec frees the results it made when there is no match; the C strings
are new[]/delete[].
- NUL terminators are written as <char>0, MatchResults loses its unused
indices_ field and its null defaults, and ArrayBuffer never holds null and
throws on a detached buffer.
Wrong results.
- search returns where the match starts ("abcdef".search(/cd/) was 3).
- join keeps the separator after a leading empty string.
- fill fills the array itself, as in JavaScript, where it filled a copy;
tests/array_class_string.ts expected the copy and is updated.
- findLast returns undefined when nothing matches, not -1.
- lastIndexOf takes a fromIndex at or past the end as the last element, and
one before the start finds nothing.
- Math.random is spread over [0, 1) on Windows: it assumed a 31-bit rand(),
and RAND_MAX there is 32767.
Tested in release on Windows x64 for gc, rc, none and own, compiled and under
the JIT. Debug, Linux, x86 and wasm were not built locally.
Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
This was referenced Nov 14, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Came out of a review of the library for slow paths and for null handling. Three groups of changes.
Speed
A string's
.lengthis astrlencall and an array'sshift()moves every remaining element; both sat inside loops. Measured under the release JIT (gc, Windows x64):toUpperCase, 200k charsJSON.stringifyof a 200k-char stringarr.join(","), 20k itemsarr.sort()default, 8k intsarr.sort(cmp), 8k intsflatMap, 80k inner elementsMessageQueue.take, 80k itemsArray<T>.spliceremoving 10, 200 times on 100kjoinbuilds its parts and allocates once.sortis a stable merge sort of positions, applied by exchanges (so-mm=ownstill sees elements only change places). It was a selection sort.flatMap,toSplicedandArray<T>.pushmove elements out with one reverse and pops.MessageQueue.takepops from an outbox refilled from the inbox.Array<T>.unshiftandspliceinserts keep the native move for fewer than 8 items and rotate for more; a single-item insert costs what it did.Null and crashes
Headers: the private methods now come after the public ones.lib.d.tsdeclares only the public ones and a program finds a method by its place, so every call ran a method two places off andgetreturned garbage.getnow returnsstring | null.RegExp.splitis declared inlib.d.ts(the same kind of mismatch).replaceAllwith an invalid pattern throws aSyntaxError; it was an access violation.execandtestwithlastIndexpast the end fail the match and reset it to 0; it was an access violation.regexp_execno longer leaks its results on a failed match.<char>0;MatchResultsloses an unused field and its null defaults;ArrayBuffernever holds null and throws on a detached buffer.Wrong results
searchreturns where the match starts ("abcdef".search(/cd/)was 3).joinkeeps the separator after a leading empty string.fillfills the array itself, as in JavaScript; it filled a copy. Behaviour change:tests/array_class_string.tsexpected the copy and is updated.findLastreturnsundefinedwhen nothing matches, not -1.lastIndexOftreats afromIndexat or past the end as the last element, and one before the start finds nothing.Math.randomis spread over [0, 1) on Windows (it assumed a 31-bitrand()).Not in this PR
JSON.parseof one object with thousands of keys is still quadratic (61 ms at 8,000 keys).String.matchstill returns null throughstring[]: the nullable type broke two existing tests at compile time.// @strict-null falsestays inlib.ts.Testing
scripts/check_declarations.ps1passes.ArrayBufferchanges compile but were not executed (programs cannot name the class yet).🤖 Generated with Claude Code